package org.example.gateway.filter;

import com.alibaba.fastjson.JSON;
import org.example.gateway.config.RabbitMQConfig;
import org.example.gateway.utils.FileUtil;
import org.example.gateway.utils.RedisUtil;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

@Component
public class AuthFilter implements GlobalFilter, Ordered {
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        // 校验令牌的有效性
        HttpHeaders headers = request.getHeaders();
        List<String> authorization = headers.get("Authorization");
        if (null == authorization) {
            // 拒绝访问
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        // 从redis中查询
        String token = authorization.get(0);
        Boolean hasToken = redisTemplate.hasKey("auth_" + token);
        if (!hasToken) {
            // 拒绝访问
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        // 限速
        Boolean limiterRes = null;
        try {
            limiterRes = this.limiter(token);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (!limiterRes) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
            return response.setComplete();
        }

        return chain.filter(exchange);
    }

    /**
     * 根据token进行限流
     * @param token 限流的token
     * @return 是否通过
     * @throws IOException
     */
    public Boolean limiter(String token) throws IOException {
        Integer qps = (Integer) redisTemplate.opsForValue().get("auth_"+token);

        Integer intervalPerTokens = 1000/qps;
        long curTime = System.currentTimeMillis();
        Integer initTokens = 10;
        Integer bucketMaxTokens = 100;
        Integer resetBucketInterval = 1000;

        // 执行 lua 脚本
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        // 指定 lua 脚本
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("limiterLuaScript.lua")));
        // 指定返回类型
        redisScript.setResultType(Long.class);
        // 参数一：redisScript，参数二：key列表，参数三：arg（可多个）
        Long result = (Long) redisTemplate.execute(redisScript, Collections.singletonList("limiter_"+token), intervalPerTokens,
                curTime, initTokens, bucketMaxTokens, resetBucketInterval);

        Integer isPass = 1;
        if (result == null || result == 0) {
            isPass = 0;
        }

        // 将限流情况发到消息队列中用作后续的统计
        HashMap<String, Object> limiterData = new HashMap<>();
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String currentTime = simpleDateFormat.format(date);
        limiterData.put("time", currentTime);
        limiterData.put("token", token);
        limiterData.put("isPass", isPass);
        String limiterDataStr = JSON.toJSONString(limiterData);
        rabbitTemplate.convertAndSend(RabbitMQConfig.ITEM_TOPIC_EXCHANGE,"item.limiter",limiterDataStr);

        return isPass == 1;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
